package com.lambkit.component.nacos;

import java.util.List;
import java.util.Properties;

import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingFactory;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.listener.EventListener;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.jfinal.kit.StrKit;
import com.lambkit.common.bean.LambkitBeanManager;
import com.lambkit.node.Node;
import com.lambkit.node.NodeManager;

public class NacosClient {

	private NacosConfig config;

	public NacosClient(NacosConfig config) {
		this.setConfig(config);
	}

	/**
	 * 获取配置
	 * 
	 * @param dataId    配置 ID
	 * @param group     配置分组
	 * @param timeoutMs 读取配置超时时间，单位 ms，推荐值 3000。
	 * @return 配置信息
	 * @throws NacosException
	 */
	public String getConfig(String dataId, String group) throws NacosException {
		if (config != null && config != null && config.isUsable()) {
			Properties properties = new Properties();
			properties.put("serverAddr", config.getAddress());
			if (StrKit.notBlank(config.getName())) {
				properties.put("namespace", config.getName());
			}
			ConfigService configService = NacosFactory.createConfigService(properties);
			return configService.getConfig(dataId, group, 5000);
		}
		return null;
	}

	/**
	 * 监听配置 如果希望 Nacos 推送配置变更，可以使用 Nacos 动态监听配置接口来实现。
	 * 
	 * @param dataId 配置 ID，
	 * @param group  配置分组
	 * @return
	 * @throws NacosException
	 */
	public String addListener(String dataId, String group) throws NacosException {
		if (config != null && config.isUsable()) {
			Properties properties = new Properties();
			properties.put("serverAddr", config.getAddress());
			if (StrKit.notBlank(config.getName())) {
				properties.put("namespace", config.getName());
			}
			ConfigService configService = NacosFactory.createConfigService(properties);
			return configService.getConfig(dataId, group, 5000);
		}
		return null;
	}

	/**
	 * 删除监听 取消监听配置，取消监听后配置不会再推送。
	 * 
	 * @param dataId 配置 ID，
	 * @param group  配置分组
	 * @throws NacosException
	 */
	public void removeListener(String dataId, String group, Listener listener) throws NacosException {
		if (config != null && config.isUsable()) {
			Properties properties = new Properties();
			properties.put("serverAddr", config.getAddress());
			if (StrKit.notBlank(config.getName())) {
				properties.put("namespace", config.getName());
			}
			ConfigService configService = NacosFactory.createConfigService(properties);
			configService.removeListener(dataId, group, listener);
		}
	}

	/**
	 * 发布配置
	 * 
	 * @param dataId  配置 ID，
	 * @param group   配置分组
	 * @param content 配置文件内容
	 * @return boolean
	 * @throws NacosException
	 */
	public boolean publishConfig(String dataId, String group, String content) throws NacosException {
		if (config != null && config.isUsable()) {
			Properties properties = new Properties();
			properties.put("serverAddr", config.getAddress());
			if (StrKit.notBlank(config.getName())) {
				properties.put("namespace", config.getName());
			}
			ConfigService configService = NacosFactory.createConfigService(properties);
			return configService.publishConfig(dataId, group, content);
		}
		return false;
	}

	/**
	 * 删除配置
	 * 
	 * @param dataId 配置 ID，
	 * @param group  配置分组
	 * @return boolean
	 * @throws NacosException
	 */
	public boolean removeConfig(String dataId, String group) throws NacosException {
		if (config != null && config.isUsable()) {
			Properties properties = new Properties();
			properties.put("serverAddr", config.getAddress());
			if (StrKit.notBlank(config.getName())) {
				properties.put("namespace", config.getName());
			}
			ConfigService configService = NacosFactory.createConfigService(properties);
			return configService.removeConfig(dataId, group);
		}
		return false;
	}

	/**
	 * 使用统一配置注册实例 注册一个实例到服务。
	 * 
	 * @param serviceName 服务名
	 * @param groupName   组名
	 * @param ip          服务实例IP
	 * @param port        服务实例端口
	 * @param clusterName 集群名
	 * @throws NacosException
	 */
	public void registerInstance(String serviceName) throws NacosException {
		if (config != null && config.isUsable()) {
			Properties properties = new Properties();
			properties.put("serverAddr", config.getAddress());
			if (StrKit.notBlank(config.getName())) {
				properties.put("namespace", config.getName());
			}
			NamingService naming = NamingFactory.createNamingService(properties);
			Node node = NodeManager.me().getNode();
			String ip = node.getIp();
			int port = LambkitBeanManager.me().getPort();
			String clusterName = config.getClusterName();
			String groupName = config.getGroup();
			if (StrKit.isBlank(clusterName)) {
				naming.registerInstance(serviceName, groupName, ip, port);
			} else {
				naming.registerInstance(serviceName, groupName, ip, port, clusterName);
			}
		}
	}

	/**
	 * 使用统一配置注销实例 删除服务下的一个实例。
	 * 
	 * @param serviceName
	 * @throws NacosException
	 */
	public void deregisterInstance(String serviceName) throws NacosException {
		if (config != null && config.isUsable()) {
			Properties properties = new Properties();
			properties.put("serverAddr", config.getAddress());
			if (StrKit.notBlank(config.getName())) {
				properties.put("namespace", config.getName());
			}
			NamingService naming = NamingFactory.createNamingService(properties);
			Node node = NodeManager.me().getNode();
			String ip = node.getIp();
			int port = LambkitBeanManager.me().getPort();
			String clusterName = config.getClusterName();
			String groupName = config.getGroup();
			if (StrKit.isBlank(clusterName)) {
				naming.deregisterInstance(serviceName, groupName, ip, port);
			} else {
				naming.deregisterInstance(serviceName, groupName, ip, port, clusterName);
			}
		}
	}

	/**
	 * 获取全部实例 获取服务下的所有实例。
	 * 
	 * @param serviceName
	 * @return
	 * @throws NacosException
	 */
	public List<Instance> getAllInstances(String serviceName) throws NacosException {
		if (config != null && config.isUsable()) {
			Properties properties = new Properties();
			properties.put("serverAddr", config.getAddress());
			if (StrKit.notBlank(config.getName())) {
				properties.put("namespace", config.getName());
			}
			NamingService naming = NamingFactory.createNamingService(properties);
			String groupName = config.getGroup();
			return naming.getAllInstances(serviceName, groupName);
		}
		return null;
	}

	/**
	 * 获取健康或不健康实例列表 根据条件获取过滤后的实例列表。
	 * 
	 * @param nacosIP     nacos 的ip
	 * @param serviceName 服务名
	 * @param healthy     是否健康
	 * @return 实体Instance
	 * @throws NacosException
	 */
	public List<Instance> selectInstances(String serviceName, boolean healthy) throws NacosException {
		if (config != null && config.isUsable()) {
			Properties properties = new Properties();
			properties.put("serverAddr", config.getAddress());
			if (StrKit.notBlank(config.getName())) {
				properties.put("namespace", config.getName());
			}
			NamingService naming = NamingFactory.createNamingService(properties);
			return naming.selectInstances(serviceName, healthy);
		}
		return null;
	}

	/**
	 * 获取健康或不健康实例列表
	 * 
	 * @param nacosIP     nacos 的ip
	 * @param serviceName 服务名
	 * @param clusters    list集群名
	 * @param healthy     是否健康
	 * @return 实体Instance
	 * @throws NacosException
	 */
	public List<Instance> selectInstances(String serviceName, List<String> clusters, boolean healthy)
			throws NacosException {
		if (config != null && config.isUsable()) {
			Properties properties = new Properties();
			properties.put("serverAddr", config.getAddress());
			if (StrKit.notBlank(config.getName())) {
				properties.put("namespace", config.getName());
			}
			NamingService naming = NamingFactory.createNamingService(properties);
			return naming.selectInstances(serviceName, clusters, healthy);
		}
		return null;
	}

	/**
	 * 根据负载均衡算法随机获取一个健康实例
	 * 
	 * @param nacosIP     nacos 的ip
	 * @param serviceName 服务名
	 * @return 实体Instance
	 * @throws NacosException
	 */
	public Instance selectOneHealthyInstance(String serviceName) throws NacosException {
		if (config != null && config.isUsable()) {
			Properties properties = new Properties();
			properties.put("serverAddr", config.getAddress());
			if (StrKit.notBlank(config.getName())) {
				properties.put("namespace", config.getName());
			}
			NamingService naming = NamingFactory.createNamingService(properties);
			return naming.selectOneHealthyInstance(serviceName);
		}
		return null;
	}

	/**
	 * 根据负载均衡算法随机获取一个健康实例
	 * 
	 * @param nacosIP     nacos 的ip
	 * @param serviceName 服务名
	 * @param clusters    list集群名
	 * @return 实体Instance
	 * @throws NacosException
	 */
	public Instance selectOneHealthyInstance(String serviceName, List<String> clusters) throws NacosException {
		if (config != null && config.isUsable()) {
			Properties properties = new Properties();
			properties.put("serverAddr", config.getAddress());
			if (StrKit.notBlank(config.getName())) {
				properties.put("namespace", config.getName());
			}
			NamingService naming = NamingFactory.createNamingService(properties);
			return naming.selectOneHealthyInstance(serviceName, clusters);
		}
		return null;
	}

	/**
	 * 监听服务 监听服务下的实例列表变化。
	 * 
	 * @param nacosIP     nacos 的ip
	 * @param serviceName 服务名
	 * @throws NacosException
	 */
	public void subscribe(String serviceName, EventListener listener) throws NacosException {
		if (config != null && config.isUsable()) {
			Properties properties = new Properties();
			properties.put("serverAddr", config.getAddress());
			if (StrKit.notBlank(config.getName())) {
				properties.put("namespace", config.getName());
			}
			NamingService naming = NamingFactory.createNamingService(properties);
			naming.subscribe(serviceName, listener);
		}
	}

	/**
	 * 取消监听服务 取消监听服务下的实例列表变化。
	 * 
	 * @param nacosIP     nacos 的ip
	 * @param serviceName 服务名
	 * @throws NacosException
	 */
	public void unsubscribe(String serviceName) throws NacosException {
		if (config != null && config.isUsable()) {
			Properties properties = new Properties();
			properties.put("serverAddr", config.getAddress());
			if (StrKit.notBlank(config.getName())) {
				properties.put("namespace", config.getName());
			}
			NamingService naming = NamingFactory.createNamingService(properties);
			naming.unsubscribe(serviceName, event -> {
			});
		}
	}

	public NacosConfig getConfig() {
		return config;
	}

	public void setConfig(NacosConfig config) {
		this.config = config;
	}
}
